package flare.animate
{
  import flare.animate.interpolate.Interpolator;
  import flare.util.Property;

  import flash.display.DisplayObject;
  
  import flare.vis.data.EdgeSprite;
  import flare.vis.data.NodeSprite;

  /**
   * Transition that interpolates (in-be<em>tweens</em>) properties
   * of a target object over a time interval. The <tt>values</tt> property
   * represents the set of properties to tween and their final values. Any
   * arbitrary property (not just visual properties) can be tweened. The
   * Tween class handles tweening of Numbers, colors, Dates, Points,
   * Rectangles, numeric Arrays, and Vector.<Object> objects containing Numbers.
   * Properties of other types are simply swapped when then Transition
   * half-completes. Tweening for custom types is possible, see the
   * <tt>flare.animate.interpolate.Interpolator</tt> class for more.
   * 
   * <p>Starting values are automatically determined from the tweened object.
   * Once determined, these starting values are stored to allow both forward
   * and backward playback. Use the <tt>reset</tt> method to force a tween to
   * redetermine the starting values the next time it is played. Tweens also
   * provide a <code>remove</code> flag for DisplayObjects. When set to true,
   * a display object will be removed from the display list at the end of the
   * tween. Note that playing the tween is reverse will not revert this
   * removal.</p>
   * 
   * <p>Internally, a Tween creates a set of Interpolator objects to compute
   * intermediate values for each property included in <tt>values</tt>. Note
   * that property names can involve nested properties. For example,
   * <tt>{"filters[0].blurX":5}</tt> is a valid tweening property, as both
   * array access (<tt>[]</tt>) and property access (<tt>.</tt>) syntax are
   * supported.</p>
   * 
   * <p>To manage a collection of objects being tweened simultaneously, use a
   * <tt>Transitioner</tt> object.</p>
   */
  public class Tween extends Transition
  {
    // -- Properties ------------------------------------------------------

    private var _interps:Vector.<Object> = new Vector.<Object>();
    private var _target:Object;
    private var _from:Object;
    private var _remove:Boolean = false;
    private var _visible:Object = null;
    private var _values:Object;

    /** The target object whose properties are tweened. */
    public function get target():Object
    {
      return _target;
    }

    public function set target(t:Object):void
    {
      _target = t;
    }

    /** Flag indicating if the target object should be removed from the
     *  display list at the end of the tween. Only applies when the target
     *  is a <code>DisplayObject</code>. */
    public function get remove():Boolean
    {
      return _remove;
    }

    public function set remove(b:Boolean):void
    {
      _remove = b;
    }

    /** The properties to tween and their target values. */
    public function get values():Object
    {
      return _values;
    }

    public function set values(o:Object):void
    {
      _values = o;
    }

    /** Optional starting values for tweened properties. */
    public function get from():Object
    {
      return _from;
    }

    public function set from(s:Object):void
    {
      _from = s;
    }


    // - Methods ----------------------------------------------------------

    /**
     * Creates a new Tween with the specified parameters.
     * @param target the target object
     * @param duration the duration of the tween, in seconds
     * @param values the properties to tween and their target values
     * @param remove a display list removal flag (for
     *  <code>DisplayObject</code> target objects
     * @param easing the easing function to use
     */
    public function Tween(target:Object, duration:Number = 1, values:Object = null, remove:Boolean = false,
      easing:Function = null)
    {
      super(duration, 0, easing);

      _target = target;
      _remove = remove;
      _values = values == null ? {
      } : values;

      _from = {
      };
    }

    /** @inheritDoc */
    public override function dispose():void
    {
      // reclaim any old interpolators
      while(_interps.length > 0)
      {
        Interpolator.reclaim(_interps.pop());
      }

      // remove all target values
      for(var name:String in _values)
      {
        delete _values[name];
      }
      _visible = null;
      _remove = false;
      _target = null;
    }

    /**
     * Sets up this tween by creating interpolators for each tweened
     * property.
     */
    protected override function setup():void
    {
      // reclaim any old interpolators
      while(_interps.length > 0)
      {
        Interpolator.reclaim(_interps.pop());
      }

      // build interpolators
      var vc:Object, v0:Object, v1:Object;

      for(var name:String in _values)
      {
        // create interpolator only if start/cur/end values don't match
        vc = Property.$(name).getValue(_target);
        v0 = _from.hasOwnProperty(name) ? _from[name] : vc;
        v1 = _values[name];

        if(vc != v1 || vc != v0)
        {
          if(name == "visible")
          {
            // special handling for visibility
            _visible = Boolean(v1);
          }

          else
          {
            _interps.push(Interpolator.create(_target, name, v0, v1));
          }
        }
      }
    }

    /**
     * Updates target object visibility, if appropriate.
     */
    protected override function start():void
    {
      // set visibility
      var item:DisplayObject = _target as DisplayObject;

      /** @Patch Frederick Giasson - 17 September April 2010 */
      /** Path to enable he show/hide hability for the relation browser */
      
      if(item != null && Boolean(_visible))
      item.visible = true;
      
      /** endpatch */
    }

    /**
     * Steps the tween, updating the tweened properties.
     */
    internal override function step(ef:Number):void
    {
      // run the interpolators
      for each(var i:Interpolator in _interps)
      {
        i.interpolate(ef);
      }
    }

    /**
     * Ends the tween, updating target object visibility and display
     * list membership, if appropriate.
     */
    protected override function end():void
    {
      // set visibility, remove from display list if requested
      var item:DisplayObject = _target as DisplayObject;

      if(item != null)
      {
        if(_remove && item.parent != null)
        item.parent.removeChild(item);

        if(_visible != null)
        {
          item.visible = Boolean(_visible);
        }
        
        
        /** @Patch Frederick Giasson - 17 September April 2010 */
        /** Path to enable he show/hide hability for the relation browser */
        /*if(item is EdgeSprite)
        {
          for each(var hiddenNode:String in item.parent.parent.parent.parent.parent.parent.parent.hiddenNodes)
          {
            if(item.source.data.type == hiddenNode)
            {
              item.visible = false;
              break;
            }
            
            if(item.target.data.type == hiddenNode)
            {
              item.visible = false;
              break;
            }       
          }
          
          for each(var hiddenEdge:String in item.parent.parent.parent.parent.parent.parent.parent.hiddenEdges)
          {
            if(item.data.type == hiddenEdge)
            {
              item.visible = false;
              break;
            }
            
            if(item.data.type == hiddenEdge)
            {
              item.visible = false;
              break;
            }       
          }          
        }  
        
        if(item is NodeSprite)
        {      
          if(item.parent.parent.parent.parent.parent.parent.parent.hiddenNodes.indexOf(item.data.type) != -1)
          {
            item.visible = false;
          }
        }*/
        
        /** @endpatch */
      }
    }
  } // end of class Tween
}